/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.zzdt4j.common.executor.support;

import cn.zzdt4j.common.extension.spi.ServiceLoaderRegistry;
import lombok.Getter;

import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Stream;

/**
 * Rejected policy type enum
 *
 * @author by <a href="mailto:ligang941012@gmail.com">gang.Li</a>
 * @since 2023/10/11 13:44
 */

public enum RejectedPolicyTypeEnum {

    CALLER_RUNS_POLICY(1, "CallerRunsPolicy", new ThreadPoolExecutor.CallerRunsPolicy()),
    ABORT_POLICY(2, "AbortPolicy", new ThreadPoolExecutor.AbortPolicy()),
    DISCARD_POLICY(3, "DiscardPolicy", new ThreadPoolExecutor.DiscardPolicy()),
    DISCARD_OLDEST_POLICY(4, "DiscardOldestPolicy", new ThreadPoolExecutor.DiscardOldestPolicy()),
    RUNS_OLDEST_TASK_POLICY(5, "RunSOldestTaskPolicy", new RunsOldestTaskPolicy()),
    SYNC_PUT_QUEUE_POLICY(6, "SyncPutQueuePolicy", new SyncPutQueuePolicy()),
    ;

    @Getter
    private final Integer type;
    @Getter
    private final String name;

    private RejectedExecutionHandler rejectedExecutionHandler;

    RejectedPolicyTypeEnum(Integer type, String name, RejectedExecutionHandler rejectedExecutionHandler) {
        this.type = type;
        this.name = name;
        this.rejectedExecutionHandler = rejectedExecutionHandler;
    }

    static {
        ServiceLoaderRegistry.register(CustomRejectedExecutionHandler.class);
    }

    public static RejectedExecutionHandler createPolicy(String name) {
        RejectedPolicyTypeEnum rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
                .filter(each -> Objects.equals(each.name, name))
                .findFirst()
                .orElse(null);

        if (rejectedTypeEnum != null) {
            return rejectedTypeEnum.rejectedExecutionHandler;
        }

        // SPI
        Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry.getSingletonServiceInstances(CustomRejectedExecutionHandler.class);

        Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
                .filter(each -> Objects.equals(name, each.getName()))
                .map(CustomRejectedExecutionHandler::generateRejected)
                .findFirst();
        return customRejected.orElse(ABORT_POLICY.rejectedExecutionHandler);
    }

    public static RejectedExecutionHandler createPolicy(int type) {
        Optional<RejectedExecutionHandler> rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
                .filter(each -> Objects.equals(type, each.type))
                .map(each -> each.rejectedExecutionHandler)
                .findFirst();

        return rejectedTypeEnum.orElseGet(() -> {
            // SPI
            Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry.getSingletonServiceInstances(CustomRejectedExecutionHandler.class);

            Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
                    .filter(each -> Objects.equals(type, each.getType()))
                    .map(CustomRejectedExecutionHandler::generateRejected)
                    .findFirst();
            return customRejected.orElse(ABORT_POLICY.rejectedExecutionHandler);
        });
    }

    public static String getRejectedNameByType(int type) {
        return createPolicy(type).getClass().getSimpleName();
    }

}
